#include "client.h"
#include "util.h"
#include "../gpu/gpu.h"
#include "../gpu/shader/defaultShader.h"
#include "../math/math.h"
#include <iostream>
#include <stdlib.h>
#include <stdexcept>
#include <string.h>
#include <wayland-client.h>
#include "xdg-shell-client-protocol.h"
//// wayland 通过监听out 监听屏幕信息
#define HEIGHT 800
#define WIDTH 800

void ClientState::registry_global(struct wl_registry *registry, uint32_t name,
                                  const char *interface, uint32_t version)
{
    if (strcmp(interface, wl_compositor_interface.name) == 0)
    {
        wl_compositor = static_cast<struct wl_compositor *>(
            wl_registry_bind(registry, name, &wl_compositor_interface, 1));
    }
    else if (strcmp(interface, xdg_wm_base_interface.name) == 0)
    {
        xdg_wm_base = static_cast<struct xdg_wm_base *>(
            wl_registry_bind(registry, name, &xdg_wm_base_interface, 1));
        xdg_wm_base_add_listener(xdg_wm_base,
                                 &xdg_wm_base_listener, this);
    }
    else if (strcmp(interface, wl_shm_interface.name) == 0)
    {
        wl_shm = static_cast<struct wl_shm *>(
            wl_registry_bind(registry, name, &wl_shm_interface, 1));
    }
}
void ClientState::frame_handle_done(struct wl_callback *cb, uint32_t time)
{
    wl_callback_destroy(cb); // 必须销毁旧回调

    struct wl_callback *new_cb = wl_surface_frame(wl_surface);
    wl_callback_add_listener(new_cb, &frame_listener, this);
    // 绘制新帧并提交
    current_buffer = 1 - current_buffer;
    draw_frame(buffer_data[current_buffer]);
    wl_surface_attach(wl_surface,
                      buffers[current_buffer], 0, 0);
    wl_surface_damage(wl_surface, 0, 0, WIDTH, HEIGHT); // 标记需要重绘的区域
    wl_surface_commit(wl_surface);
    wl_display_flush(wl_display);
}

ClientState::ClientState()
{
    // 1.添加服务端（weston/Gnome）回调处理函数
    xdg_surface_listener.configure = &static_configure; // 处理窗口尺寸/状态变更事件（如窗口大小调整、最大化等）
    wl_buffer_listener.release = &wl_buffer_release;    // 管理图形缓冲区的释放事件, 通过wl_buffer_release回调回收buffer供复用，防止内存泄漏
    frame_listener.done = &static_frame_handle_done;    // 实现垂直同步（VSync）控制通过static_frame_handle_done触发重绘循环，通常内部调用wl_callback_destroy()并注册新回调
    xdg_wm_base_listener.ping = &xdg_wm_base_ping;      // 维持客户端存活检测
    registry_listener.global = &static_registry_global; // 发现服务器提供的全局对象（核心功能入口）
    registry_listener.global_remove = nullptr;
    wl_display = wl_display_connect(nullptr);
    if (!wl_display)
        throw std::runtime_error("Failed to connect to Wayland display");

    // 2.注册监听或者获取全局对象
    wl_registry = wl_display_get_registry(wl_display);
    wl_registry_add_listener(wl_registry, &registry_listener, this);

    wl_display_roundtrip(wl_display);
    create_surface();
    create_xdg_surface();
    setup_frame_callback();
    init_buffers();
}
ClientState::~ClientState()
{
    if (xdg_toplevel)
        xdg_toplevel_destroy(xdg_toplevel);
    if (xdg_surface)
        xdg_surface_destroy(xdg_surface);
    if (wl_surface)
        wl_surface_destroy(wl_surface);
    if (wl_registry)
        wl_registry_destroy(wl_registry);
    if (wl_display)
        wl_display_disconnect(wl_display);
    int size = WIDTH * HEIGHT * 4;
    for (int i = 0; i < 2; i++)
    {
        wl_buffer_destroy(buffers[i]);
        munmap(buffer_data[i], size);
    }
}
void ClientState::create_surface()
{
    wl_surface = wl_compositor_create_surface(wl_compositor);
    if (!wl_surface)
        throw std::runtime_error("Failed to create surface");
}

void ClientState::create_xdg_surface()
{
    xdg_surface = xdg_wm_base_get_xdg_surface(xdg_wm_base, wl_surface);
    xdg_surface_add_listener(xdg_surface, &xdg_surface_listener, this);
    xdg_toplevel = xdg_surface_get_toplevel(xdg_surface);
}

math::Matrix<float, 4, 4> perspectiveMatrix = math::perspective(60.0f, (float)800 / (float)600, 0.1f, 200.0f);
math::Matrix<float, 4, 4> orthographicMatrix = math::orthographic(-0.1f, 0.1f, -0.1f, 0.1f, -0.1f, 0.1f);
math::mat4f modelMatrix;
math::mat4f viewMatrix;
float angle = 0.0f;
float cameraPos = 5.0f;
DefaultShader *shader = nullptr;
uint32_t ebo = 0;
uint32_t vao = 0;
void prepare()
{
    shader = new DefaultShader();
    perspectiveMatrix = math::perspective(60.0f, (float)WIDTH / (float)HEIGHT, 0.1f, 100.0f);
    math::Matrix<float, 4, 4> v = {
        {1, 0, 0, 0},
        {0, 1, 0, 0},
        {0, 0, 1, 0},
        {0, 0, 0, 1}};
    auto cameraModelMatrix = math::translate(v, math::vec3f{0.0f, 0.0f, 3.0f});
    viewMatrix = cameraModelMatrix;

    float positions[] = {
        -0.5f,
        -0.5f,
        0.0f,
        -0.5f,
        0.5f,
        0.0f,
        0.5f,
        -0.5f,
        0.0f,
    };

    float colors[] = {
        1.0f,
        0.0f,
        0.0f,
        1.0f,
        0.0f,
        1.0f,
        0.0f,
        1.0f,
        0.0f,
        0.0f,
        1.0f,
        1.0f,
    };

    float uvs[] = {
        0.0f,
        0.0f,
        0.0f,
        1.0f,
        1.0f,
        0.0f,
    };

    uint32_t indices[] = {0, 1, 2};
    ebo = sgl->genBuffer();
    sgl->bindBuffer(ELEMENT_ARRAY_BUFFER, ebo);
    sgl->bufferData(ELEMENT_ARRAY_BUFFER, sizeof(uint32_t) * 3, indices);
    sgl->bindBuffer(ELEMENT_ARRAY_BUFFER, 0);

    vao = sgl->genVertexArray();
    sgl->bindVertexArray(vao);
    sgl->printVAO(vao);
    auto positionVbo = sgl->genBuffer();
    sgl->bindBuffer(ARRAY_BUFFER, positionVbo);
    sgl->bufferData(ARRAY_BUFFER, sizeof(float) * 9, positions);
    sgl->vertexAttributePointer(0, 3, 3 * sizeof(float), 0);

    auto colorVbo = sgl->genBuffer();
    sgl->bindBuffer(ARRAY_BUFFER, colorVbo);
    sgl->bufferData(ARRAY_BUFFER, sizeof(float) * 12, colors);
    sgl->vertexAttributePointer(1, 4, 4 * sizeof(float), 0);

    auto uvVbo = sgl->genBuffer();
    sgl->bindBuffer(ARRAY_BUFFER, uvVbo);
    sgl->bufferData(ARRAY_BUFFER, sizeof(float) * 6, uvs);
    sgl->vertexAttributePointer(2, 2, 2 * sizeof(float), 0);

    sgl->bindBuffer(ARRAY_BUFFER, 0);
    sgl->bindVertexArray(0);
    sgl->printVAO(vao);
}

void ClientState::init_buffers()
{
    int stride = WIDTH * 4;
    int size = stride * HEIGHT;

    for (int i = 0; i < 2; i++)
    {
        // 1.在系统的虚拟文件系统（通常是 /dev/shm）中创建或打开一个命名共享内存对象。该对象表现为一个特殊的文件，但实际数据存储在内存中而非磁盘。并给该
        // 命名共享内存对象size 大小
        int fd = allocate_shm_file(size);
        // 2.通过 mmap() 将共享内存映射到进程的地址空间
        buffer_data[i] = static_cast<uint32_t *>(
            mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0));
        // 3.创建一个共享内存池
        // 建立共享内存与 Wayland 协议的关联，使多个客户端/服务端可安全访问该内存区域
        // fd指向的那块共享内存
        wl_shm_pool *pool = wl_shm_create_pool(wl_shm, fd, size);
        // 4.创建一个缓冲区
        /* dma-buf
        作用：
        在共享内存池中划分一块区域作为图形渲染缓冲区
        关键参数：
        pool：上一步创建的共享内存池
        0：缓冲区在共享内存中的起始偏移量（0 表示从开头使用）
        width/height：缓冲区的像素宽高（如 1920x1080）
        stride：每行像素的字节数（通常 = width × 4，因 ARGB8888 每像素占 4 字节）
        WL_SHM_FORMAT_ARGB8888：像素格式（含透明度通道的 32 位色）
        零拷贝渲染
        通过 mmap() 将共享内存映射到进程空间后，直接写入像素数据即可显示
        */
        buffers[i] = wl_shm_pool_create_buffer(pool, 0, WIDTH, HEIGHT,
                                               stride, WL_SHM_FORMAT_ARGB8888);
        wl_shm_pool_destroy(pool);
        close(fd);

        wl_buffer_add_listener(buffers[i], &wl_buffer_listener, this);
    }
    buffer_initialized = true;
    prepare();
    // 绘制第一帧
    draw_frame(buffer_data[current_buffer]);
    wl_surface_attach(wl_surface,
                      buffers[current_buffer], 0, 0);
    wl_surface_commit(wl_surface);
    current_buffer = 1 - current_buffer;
}

void ClientState::setup_frame_callback()
{
    wl_callback *cb = wl_surface_frame(wl_surface);
    wl_callback_add_listener(cb, &frame_listener, this);
}

void ClientState::draw_frame(uint32_t *data)
{
    int size = WIDTH * HEIGHT * 4;
    memset(data, 0xFF, size); // 白色背景
    sgl->initSurface(WIDTH, HEIGHT, data);
    angle += 0.01f;
    math::Matrix<float, 4, 4> v = {
        {1, 0, 0, 0},
        {0, 1, 0, 0},
        {0, 0, 1, 0},
        {0, 0, 0, 1}};
    modelMatrix = math::rotate(v, angle, math::vec3f{0.0f, 1.0f, 0.0f});
    shader->mModelMatrix = modelMatrix;            // 控制模型位置
    shader->mViewMatrix = viewMatrix;              // 控制摄像机位置，
    shader->mProjectionMatrix = perspectiveMatrix; // 正交投影或者透视投影

    sgl->clear();
    sgl->useProgram(shader);
    sgl->bindVertexArray(vao);
    sgl->bindBuffer(ELEMENT_ARRAY_BUFFER, ebo);
    sgl->drawElement(DRAW_TRIANGLES, 0, 3);
}
